package com.frank.oj.manager;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.frank.model.entity.discussion.Discussion;
import com.frank.model.entity.discussion.DiscussionLike;
import com.frank.model.entity.discussion.DiscussionReport;
import com.frank.model.entity.problem.Category;
import com.frank.model.entity.problem.Problem;
import com.frank.model.entity.user.UserAcproblem;
import com.frank.oj.annotation.HCOJAccessEnum;
import com.frank.oj.common.exception.StatusFailException;
import com.frank.oj.common.exception.StatusForbiddenException;
import com.frank.oj.common.exception.StatusNotFoundException;
import com.frank.oj.config.NacosSwitchConfig;
import com.frank.oj.config.SwitchConfig;
import com.frank.oj.constant.Constants;
import com.frank.oj.exception.AccessException;
import com.frank.oj.model.vo.DiscussionVO;
import com.frank.oj.service.entity.discussion.CategoryEntityService;
import com.frank.oj.service.entity.discussion.DiscussionLikeEntityService;
import com.frank.oj.service.entity.discussion.DiscussionEntityService;
import com.frank.oj.service.entity.discussion.DiscussionReportEntityService;
import com.frank.oj.service.entity.problem.ProblemEntityService;
import com.frank.oj.service.entity.user.UserAcProblemEntityService;
import com.frank.oj.shiro.AccountProfile;
import com.frank.oj.utils.RedisUtils;
import com.frank.oj.validator.AccessValidator;
import com.frank.oj.validator.CommonValidator;
import com.frank.oj.validator.GroupValidator;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author frank
 * @Date 2024/4/7
 */
@Component
public class DiscussionManager {
    @Resource
    private DiscussionEntityService discussionEntityService;

    @Resource
    private DiscussionLikeEntityService discussionLikeEntityService;

    @Resource
    private CategoryEntityService categoryEntityService;

    @Resource
    private DiscussionReportEntityService discussionReportEntityService;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private UserAcProblemEntityService userAcProblemEntityService;

    @Resource
    private ProblemEntityService problemEntityService;

    @Resource
    private GroupValidator groupValidator;

    @Resource
    private AccessValidator accessValidator;

    @Resource
    private NacosSwitchConfig nacosSwitchConfig;

    @Resource
    private CommonValidator commonValidator;

    public IPage<Discussion> getDiscussionList(Integer limit, Integer currentPage, Integer categoryId, String pid, boolean onlyMine, String keyword, boolean admin) {

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isAdmin = SecurityUtils.getSubject().hasRole("root")
                || SecurityUtils.getSubject().hasRole("problem_admin")
                || SecurityUtils.getSubject().hasRole("admin");

        Page<Discussion> page = new Page<>(currentPage, limit);

        LambdaQueryWrapper<Discussion> queryWrapper = new LambdaQueryWrapper<>();
        if (categoryId != null) {
            queryWrapper.eq(Discussion::getCategoryId, categoryId);
        }
        if (!StringUtils.isEmpty(keyword)) {
            final String key = keyword.trim();
            queryWrapper.and(wrapper -> wrapper
                    .like(Discussion::getTitle, key).or()
                    .like(Discussion::getId, key).or()
                    .like(Discussion::getAuthor, key).or()
                    .like(Discussion::getDescription, key));
        }
        if (!StringUtils.isEmpty(pid)) {
            queryWrapper.eq(Discussion::getPid, pid); //题目关联讨论
        } else {
            queryWrapper.isNull(Discussion::getPid);//常规讨论
        }
        if (!(admin && isAdmin)) {
            queryWrapper.isNull(Discussion::getGid);
            queryWrapper.eq(Discussion::getStatus, 0); //非管理员仅获取公开讨论
        }
        queryWrapper.orderByDesc(Discussion::getTopPriority)
                .orderByDesc(Discussion::getGmtCreate)
                .orderByDesc(Discussion::getLikeNum)
                .orderByDesc(Discussion::getViewNum);
        if (onlyMine && userRole != null) {
            queryWrapper.eq(Discussion::getUid, userRole.getUid());
        }

        IPage<Discussion> discussionIPage = discussionEntityService.page(page, queryWrapper);
        List<Discussion> records = discussionIPage.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            for (Discussion discussion : records) {
                if (userRole == null) {
                    discussion.setContent(null);
                } else if (!userRole.getUid().equals(discussion.getUid())) {
                    discussion.setContent(null);
                }
            }
        }
        return discussionIPage;
    }

    public List<Category> getDiscussionCategory() {
        return categoryEntityService.list();
    }

    public DiscussionVO getDiscussion(Integer did) throws StatusNotFoundException, StatusForbiddenException, AccessException {
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();

        boolean isRoot = SecurityUtils.getSubject().hasRole("root");

        String uid = null;

        if (userRole != null) {
            uid = userRole.getUid();
        }

        DiscussionVO discussionVO = discussionEntityService.getDiscussion(did, uid);

        if (discussionVO == null) {
            throw new StatusNotFoundException("讨论不存在");
        }
        if (discussionVO.getGid() != null && uid == null) {
            throw new StatusForbiddenException("请登录后重试");
        }
        if (discussionVO.getStatus() == 1) {
            throw new StatusForbiddenException("该讨论已封禁");
        }

        if (discussionVO.getGid() != null) {
            accessValidator.validateAccess(HCOJAccessEnum.GROUP_DISCUSSION);
            if (!isRoot && !discussionVO.getUid().equals(uid) && !groupValidator.isGroupMember(uid, discussionVO.getGid())) {
                throw new StatusForbiddenException("您没有权限访问该讨论");
            }
        } else {
            accessValidator.validateAccess(HCOJAccessEnum.PUBLIC_DISCUSSION);
        }
        LambdaUpdateWrapper<Discussion> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.setSql("view_num=view_num+1").eq(Discussion::getId, discussionVO.getId());
        discussionEntityService.update(updateWrapper);
        discussionVO.setViewNum(discussionVO.getViewNum() + 1);

        return discussionVO;
    }

    public void addDiscussion(Discussion discussion) throws StatusFailException, StatusNotFoundException, StatusForbiddenException {
        //    基础校验
        commonValidator.validateContent(discussion.getTitle(), "讨论标题", 255);
        commonValidator.validateContent(discussion.getDescription(), "讨论描述", 255);
        commonValidator.validateContent(discussion.getContent(), "讨论", 65535);
        commonValidator.validateNotEmpty(discussion.getCategoryId(), "讨论分类");

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");
        boolean isProblemAdmin = SecurityUtils.getSubject().hasRole("problem_admin");
        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");

        String pid = discussion.getPid();
        if (pid != null) {
            LambdaQueryWrapper<Problem> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Problem::getProblemId, pid);
            int problemCount = problemEntityService.count(queryWrapper);
            if (problemCount == 0) {
                throw new StatusNotFoundException("该题目不存在");
            }
        }

        if (discussion.getGid() != null) {
            if (!isRoot
                    && !discussion.getUid().equals(userRole.getUid())
                    && !groupValidator.isGroupMember(userRole.getUid(), discussion.getGid())) {
                throw new StatusForbiddenException("非团队成员，无法发布团队内的讨论信息");
            }
        }

        if (!isRoot && !isProblemAdmin && !isAdmin) {
            QueryWrapper<UserAcproblem> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("uid", userRole.getUid()).select("distinct pid");
            int userAcProblemCount = userAcProblemEntityService.count(queryWrapper);

            SwitchConfig switchConfig = nacosSwitchConfig.getSwitchConfig();
            if (userAcProblemCount < switchConfig.getDefaultCreateDiscussionACInitValue()) {
                throw new StatusForbiddenException("对不起，该账户发布讨论功能未开启！系统设定提交题目通过" + switchConfig.getDefaultCreateDiscussionACInitValue() + "道以上才可发布！加油💪");
            }

            String lockKey = Constants.Account.DISCUSSION_ADD_NUM_LOCK.getCode() + userRole.getUid();
            Integer num = (Integer) redisUtils.get(lockKey);
            if (num == null) {
                redisUtils.set(lockKey, 1, 60 * 60 * 24); //一天
            } else if (num >= switchConfig.getDefaultCreateDiscussionDailyLimit()) {
                throw new StatusForbiddenException("对不起，您今天发布讨论次数已达上限！系统设定发布讨论次数" + switchConfig.getDefaultCreateDiscussionDailyLimit() + "次/天！");
            } else {
                redisUtils.incr(lockKey, 1);
            }
        }
        discussion.setAuthor(userRole.getUsername())
                .setAvatar(userRole.getAvatar())
                .setUid(userRole.getUid());

        if (SecurityUtils.getSubject().hasRole("root")) {
            discussion.setRole("root");
        } else if (SecurityUtils.getSubject().hasRole("admin")
                || SecurityUtils.getSubject().hasRole("problem_admin")) {
            discussion.setRole("admin");
        } else {
            // 如果不是管理员角色，一律重置为不置顶
            discussion.setTopPriority(false);
        }

        boolean saved = discussionEntityService.saveOrUpdate(discussion);
        if (!saved) {
            throw new StatusFailException("发布讨论失败！");
        }

    }

    public void updateDiscussion(Discussion discussion) throws StatusFailException, StatusNotFoundException, StatusForbiddenException {
        commonValidator.validateNotEmpty(discussion.getId(), "讨论ID");
        commonValidator.validateContent(discussion.getTitle(), "讨论标题", 255);
        commonValidator.validateContent(discussion.getDescription(), "讨论描述", 255);
        commonValidator.validateContent(discussion.getContent(), "讨论", 65535);
        commonValidator.validateNotEmpty(discussion.getCategoryId(), "讨论分类");

        QueryWrapper<Discussion> discussionQueryWrapper = new QueryWrapper<>();
        discussionQueryWrapper
                .select("id", "uid", "gid")
                .eq("id", discussion.getId());

        Discussion oriDiscussion = discussionEntityService.getOne(discussionQueryWrapper);
        if (oriDiscussion == null) {
            throw new StatusNotFoundException("该讨论不存在！");
        }

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");
        boolean isProblemAdmin = SecurityUtils.getSubject().hasRole("problem_admin");
        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");

        if (!isRoot
                && !oriDiscussion.getUid().equals(userRole.getUid())
                && !(oriDiscussion.getGid() != null
                && groupValidator.isGroupAdmin(userRole.getUid(), oriDiscussion.getGid()))) {
            throw new StatusForbiddenException("对不起，您无权限操作！");
        }
        UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
        discussionUpdateWrapper.set("title", discussion.getTitle())
                .set("content", discussion.getContent())
                .set("description", discussion.getDescription())
                .set("category_id", discussion.getCategoryId())
                .set(isRoot || isProblemAdmin || isAdmin,
                        "top_priority", discussion.getTopPriority())
                .eq("id", discussion.getId());

        boolean isOk = discussionEntityService.update(discussionUpdateWrapper);
        if (!isOk) {
            throw new StatusFailException("修改失败");
        }
    }

    public void removeDiscussion(Integer did) throws StatusNotFoundException, StatusForbiddenException, StatusFailException {
        LambdaQueryWrapper<Discussion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Discussion::getId, Discussion::getUid, Discussion::getGid)
                .eq(Discussion::getId, did);
        Discussion discussion = discussionEntityService.getOne(queryWrapper);
        if (discussion == null) {
            throw new StatusNotFoundException("该讨论不存在！");
        }

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");
        if (!isRoot
                && !discussion.getUid().equals(userRole.getUid())
                && !(discussion.getGid() != null
                && groupValidator.isGroupAdmin(userRole.getUid(), discussion.getGid()))) {
            throw new StatusForbiddenException("对不起，您无权限操作！");
        }

        LambdaUpdateWrapper<Discussion> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Discussion::getId, did);

        // 仅管理员和发布者能删除讨论
        if (!SecurityUtils.getSubject().hasRole("root")
                && !SecurityUtils.getSubject().hasRole("admin")
                && !SecurityUtils.getSubject().hasRole("problem_admin")) {
            updateWrapper.eq(Discussion::getUid, userRole.getUid());
        }

        boolean isOk = discussionEntityService.remove(updateWrapper);
        if (!isOk) {
            throw new StatusFailException("删除失败，无权限或者该讨论不存在");
        }

    }

    public List<Category> upsertDiscussionCategory(List<Category> categoryList) throws StatusFailException {
        List<Category> categories = categoryList.stream().filter(category ->
                category.getName() != null && !category.getName().trim().isEmpty()
        ).collect(Collectors.toList());
        boolean saved = categoryEntityService.saveOrUpdateBatch(categories);
        if (!saved) {
            throw new StatusFailException("保存失败！");
        }
        return categoryEntityService.list();
    }

    @Transactional(rollbackFor = Exception.class)
    public void addDiscussionLike(Integer did, Boolean toLike) throws StatusNotFoundException, StatusForbiddenException, StatusFailException {
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        Discussion discussion = discussionEntityService.getById(did);
        if (discussion == null) {
            throw new StatusNotFoundException("该讨论不存在！");
        }

        if (discussion.getGid() != null) {
            boolean isRoot = SecurityUtils.getSubject().hasRole("root");
            if (!isRoot && !discussion.getUid().equals(userRole.getUid())
                    && !groupValidator.isGroupMember(userRole.getUid(), discussion.getGid())) {
                throw new StatusForbiddenException("对不起，您无权限操作！");
            }
        }

        LambdaQueryWrapper<DiscussionLike> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DiscussionLike::getDid, did).eq(DiscussionLike::getUid, userRole.getUid());
        DiscussionLike discussionLike = discussionLikeEntityService.getOne(queryWrapper, false);

        if (toLike) { //点赞
            if (discussionLike == null) {
                boolean saved = discussionLikeEntityService.saveOrUpdate(new DiscussionLike()
                        .setUid(userRole.getUid())
                        .setDid(did));

                if (!saved) {
                    throw new StatusFailException("点赞失败！请重试");
                }
            }
            UpdateWrapper<Discussion> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", discussion.getId()).setSql("like_num=like_num+1");
            discussionEntityService.update(updateWrapper);

            //    信息提示给创建人
            if (!userRole.getUid().equals(discussion.getUid())) {
                discussionEntityService.updatePostLikeMsg(discussion.getUid(), userRole.getUid(), did, discussion.getGid());
            }
        } else {
            if (discussionLike != null) {
                boolean removed = discussionLikeEntityService.removeById(discussionLike.getId());
                if (!removed) {
                    throw new StatusFailException("取消点赞失败！请重试");
                }
            } else {
                throw new StatusFailException("尚未点赞！");
            }

            UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
            discussionUpdateWrapper.setSql("like_num=like_num-1").eq("id", did);
            discussionEntityService.update(discussionUpdateWrapper);
        }
    }

    public void addDiscussionReport(DiscussionReport discussionReport) throws StatusFailException {
        boolean saved = discussionReportEntityService.saveOrUpdate(discussionReport);
        if (!saved) {
            throw new StatusFailException("举报发生异常！请重试");
        }
    }
}
